﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;

using Seven.Entity;
using Seven.MsSql.Repositories;
using Seven.DataModel.ViewModels.MenuAbout;
using Seven.Tools.Extension;
using Seven.Tools.Helper;

namespace Seven.Service.Powerful
{
    public partial class SysMenuService
    {
        /// <summary>
        /// 根据菜单权限范围集合获取对应的菜单范围集合
        /// </summary>
        /// <param name="menuFunctions">菜单权限集合</param>
        /// <returns></returns>
        public IEnumerable<SysMenu> GetRangeMenus(IEnumerable<SysMenuFunction> menuFunctions)
        {
            //存储所有菜单，以便循环递归时不需要从数据库中查询
            var AllMenus = DbFactory.UnitOfWork.SysMenu.GetAllMenus();
            foreach (var item in menuFunctions)
            {
                item.Menu = AllMenus.FirstOrDefault(f => f.ID == item.MenuID);
            }

            //获得有访问权限的菜单
            var Result = menuFunctions.Where(w => w.ActionName == w.Menu.ActionName && w.Menu.IsShow).Select(s => s.Menu).ToList();

            //最终，根据拥有浏览权限的菜单，往父级菜单推，直至顶级菜单
            var ParentMenusDic = new Dictionary<int, SysMenu>();

            foreach (var item in Result)
            {
                var ParentMenu = AllMenus.FirstOrDefault(m => m.ID == item.ParentID);
                if (ParentMenu != null && !ParentMenusDic.ContainsKey(ParentMenu.ID))
                {
                    ParentMenusDic.Add(ParentMenu.ID, ParentMenu);
                    //递归获取父级菜单
                    while (ParentMenu.ParentID != 0)
                    {
                        ParentMenu = AllMenus.FirstOrDefault(m => m.ID == ParentMenu.ParentID);
                        if (ParentMenu != null && !ParentMenusDic.ContainsKey(ParentMenu.ID))
                        {
                            ParentMenusDic.Add(ParentMenu.ID, ParentMenu);
                        }
                    }
                }
            }

            Result.AddRange(ParentMenusDic.Select(s => s.Value));

            return Result;
        }

        /// <summary>
        /// 根据用户ID获取可用的菜单集合
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <param name="all">是否获取全部，默认否。若是，将忽略权限。</param>
        /// <returns></returns>
        public IEnumerable<SysMenu> GetRangeMenus(int userId, bool all = false)
        {
            IEnumerable<SysMenuFunction> menuFunctions = ServiceFactory.SysMenuFunction.GetRangeMenuFunctions(userId, all);
            if (menuFunctions.Count() == 0) { return new List<SysMenu>(); }
            return this.GetRangeMenus(menuFunctions);
        }

        /// <summary>
        /// 根据菜单功能点IDs计算可用的菜单集合
        /// </summary>
        /// <param name="functionIds"></param>
        /// <returns></returns>
        public IEnumerable<SysMenu> GetRangeMenus(IEnumerable<string> functionIds)
        {
            var functions = ServiceFactory.SysMenuFunction.GetRangeMenuFunctions(functionIds);
            return this.GetRangeMenus(functions);
        }

        #region 暂仅限内部调用的方法

        /// <summary>
        /// 根据菜单ID获取菜单名称
        /// </summary>
        /// <param name="id">菜单ID</param>
        /// <returns></returns>
        internal string GetMenuName(int id)
        {
            using (var unit = DbFactory.UnitOfWork)
            {
                return unit.SysMenu.GetMenuName(id);
            }
        }

        /// <summary>
        /// 根据菜单ID判定该菜单下是否存在子菜单
        /// </summary>
        /// <param name="menuId">菜单ID</param>
        /// <returns></returns>
        internal bool SonMenuExist(int menuId)
        {
            using (var unit = DbFactory.UnitOfWork)
            {
                return unit.SysMenu.SonMenuExist(menuId);
            }
        }

        /// <summary>
        /// 获取新的菜单Code
        /// Code严格遵循 父级Code + 自带排序号（不足两位前面补0） 的格式
        /// 若Code已存在，则当前排序号自增1（不能超过99），重新取Code
        /// </summary>
        /// <param name="parentId">父级菜单ID</param>
        /// <param name="sortNumber">自带排序号</param>
        /// <returns></returns>
        internal string GetNewMenuCode(int parentId, int sortNumber)
        {
            string Result = ""; int ResultSortNumber = 0;
            if (parentId == 0)
            {
                using (var unit = DbFactory.UnitOfWork)
                {
                    var MenuRep = unit.SysMenu;
                    var MaxSortNumber = MenuRep.GetMaxSortNumberNearLevel(parentId);
                    ResultSortNumber = (MaxSortNumber + 1) > 99 ? 1 : (MaxSortNumber + 1);
                    Result = ResultSortNumber > 9 ? ResultSortNumber.ToString() : "0" + ResultSortNumber;
                    while (MenuRep.MenuCodeExist(Result))
                    {
                        ResultSortNumber = (ResultSortNumber + 1) > 99 ? 1 : (ResultSortNumber + 1);
                        Result = ResultSortNumber > 9 ? ResultSortNumber.ToString() : "0" + ResultSortNumber;
                    }
                    return Result;
                }
            }
            else
            {
                using (var unit = DbFactory.UnitOfWork)
                {
                    var MenuRep = unit.SysMenu;
                    var ParentMenuCode = MenuRep.GetMenuCode(parentId); ResultSortNumber = sortNumber;
                    Result = ParentMenuCode + (ResultSortNumber > 9 ? ResultSortNumber.ToString() : "0" + ResultSortNumber);
                    while (MenuRep.MenuCodeExist(Result))
                    {
                        ResultSortNumber = (ResultSortNumber + 1) > 99 ? 1 : (ResultSortNumber + 1);
                        Result = ParentMenuCode + (ResultSortNumber > 9 ? ResultSortNumber.ToString() : "0" + ResultSortNumber);
                    }
                    return Result;
                }
            }
        }

        /// <summary>
        /// 根据菜单code获取其所有下级菜单
        /// </summary>
        /// <param name="menuCode">菜单code</param>
        /// <returns></returns>
        internal IEnumerable<SysMenu> GetChildrenMenusByMenuCode(string menuCode)
        {
            using (var unit = DbFactory.UnitOfWork)
            {
                return unit.SysMenu.GetChildren(menuCode);
            }
        }

        #endregion

        #region 获取ViewModel

        /// <summary>
        /// 根据父级菜单ID获取新的菜单模型
        /// </summary>
        /// <param name="parentId">父级菜单ID</param>
        /// <returns></returns>
        public MenuModel GetNewMenuModel(int parentId)
        {
            var model = new MenuModel();
            model.ParentID = parentId;
            model.ParentName = model.ParentID == 0 ? "根目录" : this.GetMenuName(model.ParentID);
            model.IconCls = SiteCore.DefaultIconClassName;
            model.IsShow = true;

            return model;
        }

        /// <summary>
        /// 根据菜单ID获取供编辑的数据模型
        /// </summary>
        /// <param name="id">菜单ID</param>
        /// <returns></returns>
        public MenuModel GetEditMenuModel(int id)
        {
            using (var unit = DbFactory.UnitOfWork)
            {
                var rep = unit.SysMenu;
                var model = rep.GetEditModelByID(id);
                model.ParentName = model.ParentID == 0 ? "根目录" : rep.GetMenuName(model.ParentID);

                return model;
            }
        }

        #endregion

        #region 提供json数据

        /// <summary>
        /// 提供完整的菜单信息json数据
        /// </summary>
        /// <param name="showRoot">是否带“根目录”</param>
        /// <returns></returns>
        public JsonResult FullMenusJsonForTree(bool showRoot = false)
        {
            IEnumerable<SysMenu> menus;

            using (var unit = DbFactory.UnitOfWork)
            {
                menus = unit.SysMenu.GetAllMenus();
            }

            if (showRoot)
            {
                var result = new List<object>();
                result.Add(new { id = 0, text = "根目录", iconCls = "icon-root", attributes = new { Code = "00", SortNumber = 0 } });
                foreach (var item in menus)
                {
                    result.Add(new
                    {
                        id = item.ID,
                        text = item.Name,
                        item.ParentID,
                        iconCls = string.IsNullOrWhiteSpace(item.IconCls) ? SiteCore.DefaultIconClassName : item.IconCls,
                        attributes = new { item.Code, item.SortNumber }
                    });
                }

                return new DataResult(result).SerializeToJsonResult();
            }
            else
            {
                return new DataResult(menus.Select(s => new
                {
                    id = s.ID,
                    text = s.Name,
                    s.ParentID,
                    iconCls = string.IsNullOrWhiteSpace(s.IconCls) ? SiteCore.DefaultIconClassName : s.IconCls,
                    attributes = new { s.Code, s.SortNumber }
                })).SerializeToJsonResult();
            }
        }

        /// <summary>
        /// 以异步方式为treegrid提供菜单信息json数据
        /// </summary>
        /// <param name="id">父级菜单ID</param>
        /// <returns></returns>
        public JsonResult MenuJsonForTreeGrid(int id)
        {
            IEnumerable<SysMenu> sons; IEnumerable<KeyValuePair<int, int>> count;
            using (var unit = DbFactory.UnitOfWork)
            {
                var rep = unit.SysMenu;
                sons = rep.GetSons(id);
                var Temp = sons.Select(s => s.ID);
                count = rep.GetSonsCount(Temp).Where(w => w.Value > 0);
            }

            return new DataResult(sons.Select(s => new
            {
                s.ID,
                s.Name,
                s.Code,
                s.SortNumber,
                iconCls = string.IsNullOrWhiteSpace(s.IconCls) ? SiteCore.DefaultIconClassName : s.IconCls,
                s.ControllerName,
                s.ActionName,
                s.IsShow,
                s.IsSystem,
                s.Remark,
                Url = Tools.Helper.UrlHelper.GetRealUrl(s.ControllerName, s.ActionName),
                state = count.Any(a => a.Key == s.ID) ? "closed" : "open"
            })).SerializeToJsonResult();
        }

        #endregion

        #region CUD操作

        /// <summary>
        /// 添加菜单，返回的结果中包含菜单对象，用于前台操作treegrid
        /// </summary>
        /// <param name="entity">菜单实体</param>
        /// <returns>操作结果</returns>
        public override OperationResult Insert(SysMenu entity)
        {
            using (var unit = DbFactory.UnitOfWork)
            {
                var rep = unit.SysMenu;

                try
                {
                    unit.BeginTransaction();
                    rep.EntityAdded(entity);

                    var msg = new string[] { "添加成功!", "添加失败!" };
                    var result = new OperationResult(unit.Commit() > 0, msg);
                    if (result.Success)
                    {
                        result.Data = new
                        {
                            state = "insert",
                            newObj = new
                            {
                                entity.ID,
                                entity.Name,
                                iconCls = string.IsNullOrWhiteSpace(entity.IconCls) ? SiteCore.DefaultIconClassName : entity.IconCls,
                                entity.Code,
                                entity.SortNumber,
                                entity.ParentID,
                                entity.ControllerName,
                                entity.ActionName,
                                Url = Tools.Helper.UrlHelper.GetRealUrl(entity.ControllerName, entity.ActionName),
                                entity.IsShow,
                                entity.Remark
                            }
                        };
                    }
                    return result;
                }
                catch (DataAccessException ex)
                {
                    unit.Rollback();
                    throw ExceptionHelper.ThrowServiceException(ex.Message);
                }
            }
        }

        /// <summary>
        /// 保存菜单，返回的结果中包含菜单对象，用于前台操作treegrid
        /// </summary>
        /// <param name="model">菜单数据模型</param>
        /// <returns>操作结果</returns>
        public JsonResult SaveMenu(MenuModel model)
        {
            SysMenu entity; JsonResult result;
            bool move = false;

            if (model.ID == 0)
            {
                entity = model.CopyTo<SysMenu>();
                entity.Code = this.GetNewMenuCode(entity.ParentID, entity.SortNumber);

                result = this.Insert(entity).SerializeToJsonResult();
            }
            else
            {
                var Menus = new List<SysMenu>();

                #region 当前菜单信息

                entity = this.GetByKey(model.ID);
                move = entity.ParentID != model.ParentID;
                string TempCode = entity.Code;
                entity.Name = model.Name;
                entity.SortNumber = model.SortNumber;
                if (move)
                {
                    //父级菜单已改变，重建Code
                    entity.ParentID = model.ParentID;
                    entity.Code = this.GetNewMenuCode(entity.ParentID, entity.SortNumber);
                }
                entity.IconCls = model.IconCls;
                entity.ControllerName = model.ControllerName;
                entity.ActionName = model.ActionName;
                entity.IsShow = model.IsShow;
                entity.IsSystem = model.IsSystem;
                entity.Remark = model.Remark;
                Menus.Add(entity);

                #endregion

                #region 因当前菜单的父级菜单有所调整而导致的子菜单Code更新

                if (move)
                {
                    //父级菜单已改变，重建子集的Code
                    var ChildrenMenu = this.GetChildrenMenusByMenuCode(TempCode);
                    if (ChildrenMenu.Count() > 0)
                    {
                        foreach (var item in ChildrenMenu)
                        {
                            item.Code = entity.Code + item.Code.Substring(TempCode.Length);
                        }
                        Menus.AddRange(ChildrenMenu);
                    }
                }

                #endregion

                OperationResult sr = this.Update(Menus);
                if (sr.Success)
                {
                    sr.Data = new
                    {
                        state = move ? "move" : "update",
                        newObj = new
                        {
                            entity.ID,
                            entity.Name,
                            iconCls = string.IsNullOrWhiteSpace(entity.IconCls) ? SiteCore.DefaultIconClassName : entity.IconCls,
                            entity.Code,
                            entity.SortNumber,
                            entity.ParentID,
                            entity.ControllerName,
                            entity.ActionName,
                            Url = Tools.Helper.UrlHelper.GetRealUrl(entity.ControllerName, entity.ActionName),
                            entity.IsShow,
                            entity.Remark
                        }
                    };
                }
                result = sr.SerializeToJsonResult();
            }

            return result;
        }

        /// <summary>
        /// 移除菜单
        /// </summary>
        /// <param name="id">菜单id</param>
        /// <returns></returns>
        public JsonResult RemoveMenu(int id)
        {
            var Menu = this.GetByKey(id);
            if (Menu == null) { return new OperationResult(false, "菜单不存在！").SerializeToJsonResult(); }
            if (this.SonMenuExist(id))
            {
                return new OperationResult(false, "存在子菜单，无法删除，操作失败！").SerializeToJsonResult();
            }

            return this.Delete(Menu).SerializeToJsonResult();
        }

        #endregion
    }
}
